﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AyFrame.Dto;
using AyFrame.IServices;
using AyFrame.NSerialLog;
using AyFrame.Strings;
using AyFrame.Dto.Auth;

namespace AyFrame.WebApi.Providers
{
    public class TokenProviderMiddleware
    {
        readonly RequestDelegate _next;
        readonly TokenProviderOptions _option;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="next">请求</param>
        /// <param name="options">配置</param>
        public TokenProviderMiddleware(RequestDelegate next,
            IOptions<TokenProviderOptions> options)
        {
            _next = next;
            _option = options.Value;
        }

        /// <summary>
        /// Invoke方法
        /// 中间件必须要有
        /// </summary>
        /// <param name="context">http请求</param>
        /// <returns></returns>
        public Task Invoke(HttpContext context)
        {
            var path = context.Request.Path.Value;
            //非Token请求
            if (path.ToUpper() != _option.Path.ToUpper())
                return _next(context);

            //Token请求
            //前端请求会有一个options请求，这种就直接返回
            if (context.Request.Method == "OPTIONS")
            {
                context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
                context.Response.StatusCode = 200;
                return context.Response.WriteAsync("ok");
            }

            //验证是否为Post方法
            if (context.Request.Method != "POST")
            {
                context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
                context.Response.StatusCode = 400;
                return context.Response.WriteAsync("Allow Post Only");
            }

            return GetToken(context);
        }

        /// <summary>
        /// 获取Token身份信息
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        async Task GetToken(HttpContext context)
        {
            try
            {
                LoginRequest lr = null;

                #region 登录信息
                //Json方式传值
                if (context.Request.ContentType.ToUpper().Equals("application/json".ToUpper()))
                {
                    lr = await context.Request.ReadFromJsonAsync<LoginRequest>();
                }
                //Form方式传值
                else if (context.Request.ContentType.ToUpper().Contains("form-data".ToUpper())
                    || context.Request.ContentType.ToUpper().Contains("form-urlencoded".ToUpper()))
                {
                    var colForm = await context.Request.ReadFormAsync();
                    lr = new LoginRequest();

                    foreach (var key in colForm.Keys)
                    {
                        if (key.ToUpper() == "CID")
                            lr.Cid = colForm[key];
                        else if (key.ToUpper() == "LOGINNAME")
                            lr.LoginName = colForm[key];
                        else if (key.ToUpper() == "PASSWORD")
                            lr.Password = colForm[key];
                    }
                }

                if (lr == null) throw new Exception("登录信息无效");
                #endregion

                var ip = context.Connection.RemoteIpAddress.ToString();
                lr.Ip = ip;

                LogHelper.LogInfo($"登录信息：{JsonConvert.SerializeObject(lr)}");

                #region 登录处理

                //登录 身份认证
                var loginResponse = await GetIdentity(lr);
                if (loginResponse.Item1 != null)
                {
                    context.Response.StatusCode = 400;
                    context.Response.ContentType = "application/json";
                    context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
                    var data = new InnerResponse { Message = loginResponse.Item1 };
                    await context.Response.WriteAsync(JsonConvert.SerializeObject(data));
                    return;
                }

                #region 生成Token

                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Issuer = _option.Issuer,
                    Audience = _option.Audience,
                    Subject = loginResponse.Item2,
                    Expires = DateTime.UtcNow.AddDays(7),
                    SigningCredentials = _option.SigningCredentials
                };

                var tokenHandler = new JwtSecurityTokenHandler();
                var jwt = tokenHandler.CreateToken(tokenDescriptor);
                var token = tokenHandler.WriteToken(jwt);

                #endregion

                var response = new TokenDto
                {
                    IsSuccess = true,
                    Token = token,
                    //expires_in = (int)_options.Expiration.TotalSeconds,
                    ApiKey = loginResponse.Item2.Claims.FirstOrDefault(m => m.Type == ClaimTypeString.ApiKey).Value
                };

                context.Response.ContentType = "application/json";
                context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
                await context.Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));

                #endregion
            }
            catch (Exception ex)
            {
                LogHelper.LogError("登录解析异常", ex);

                context.Response.StatusCode = 400;
                await context.Response.WriteAsync("用户认证失败");
            }
        }

        /// <summary>
        /// 登录   
        /// </summary>
        /// <param name="lr"></param>
        /// <returns></returns>
        async Task<(string, ClaimsIdentity)> GetIdentity(LoginRequest lr)
        {
            using (var container = Startup.Host.Services.CreateScope())
            {
                //调用Service登录接口
                IAuthService _authService = container.ServiceProvider.GetService<IAuthService>();
                LoginResponse rsp = await _authService.Token(lr);

                if (!rsp.IsSuccess) return ("登录失败", null);

                var identity = new ClaimsIdentity(new System.Security.Principal.GenericIdentity(lr.Cid + lr.LoginName, "Token"), new Claim[] { });
                identity.AddClaim(new Claim(ClaimTypeString.UserId, rsp.UserId));
                identity.AddClaim(new Claim(ClaimTypeString.ApiKey, rsp.ApiKey));
                identity.AddClaim(new Claim(ClaimTypeString.UserName, rsp.UserName));
                //identity.AddClaim(new Claim(ClaimTypeString.SiteName, lr.SiteName));
                //identity.AddClaim(new Claim(ClaimTypeString.SiteNo, lr.SiteNo));
                //identity.AddClaim(new Claim(ClaimTypeString.SiteId, lr.SiteId.ToString()));
                //identity.AddClaim(new Claim(ClaimTypeString.LoginId, lr.LoginId.ToString()));
                //identity.AddClaim(new Claim(ClaimTypeString.Uid, lr.Uid.ToString()));
                identity.AddClaim(new Claim(ClaimTypeString.Ip, rsp.Ip.ToString()));

                return (null, identity);
            }
        }
    }
}
